package mint.modules.generator.expression;

import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

import org.antlr.runtime.tree.Tree;

import mint.modules.generator.CounterManager;
import mint.modules.generator.Scope;
import mint.modules.type.Type;

public class EqualityExprNode extends Expression {
	private Expression left, right;
	private Type type;
	private int line;
	private List<String> errors;
	private String operator;

	public EqualityExprNode(Scope theParent, ExpressionType exprType, Tree equalityTree) {
		super(theParent, exprType);
		
		type = null;
		line = equalityTree.getLine();
		errors = new ArrayList<String>();
		operator = null;
		
		if (equalityTree.getText().equals("==") || 
			equalityTree.getText().equals("!="))
		{
			left = new EqualityExprNode(theParent, exprType, equalityTree.getChild(0));
			right = new RelationalExprNode(theParent, exprType, equalityTree.getChild(1));
			operator = equalityTree.getText();
		}
		else
		{
			left = new RelationalExprNode(theParent, exprType, equalityTree);
			right = null;
		}
	}

	@Override
	public void testSemantic() {
		left.testSemantic();
		type = left.getType();
		
		if (right != null)
		{
			if (getExpressionType() != ExpressionType.Right)
				errors.add(line + ": " + operator + " must be used on the right side of a expression");
			
			right.testSemantic();
			type = Type.booleanType;
			
			Type typeLeft = left.getType();
			Type typeRight = right.getType();			
			
			if (operator.equals("==") || operator.equals("!="))
			{
				if (typeLeft.equals(Type.intType) || typeLeft.equals(Type.floatType))
				{
					if (!(typeRight.equals(Type.intType) || typeRight.equals(Type.floatType)))									
						errors.add(line + ": " + operator + " must be the same type");
					
				} if (typeLeft.equals(Type.booleanType))
				{
					if(typeRight.equals(Type.booleanType))
					{
						errors.add(line + ": " + operator + " must be the same type");
					}
				} 					
			}  else
				errors.add(line + ": " + operator + " must be used on integer expressions");			
		}
	}

	@Override
	public List<String> getSemanticErrors() {
		errors.addAll(left.getSemanticErrors());
		if (right != null)
			errors.addAll(right.getSemanticErrors());
		return errors;
	}

	@Override
	public void emit(Formatter output) {
		String label1 = CounterManager.getInstance().generate("equality");
		String label2 = CounterManager.getInstance().generate("equality");
		String label3 = CounterManager.getInstance().generate("equality");
		
		left.emit(output);
		
		if (operator != null)
		{
			Type typeLeft = left.getType();
			Type typeRight = right.getType();
			
			if (typeLeft.equals(Type.intType))
			{
				if (typeRight.equals(Type.intType))
				{
					right.emit(output);
					output.format("isub\n");
				}
				else if (typeRight.equals(Type.floatType))
				{
					output.format("i2f\n");
					right.emit(output);
					output.format("fsub\n");
					output.format("f2i\n");
				}
				if (operator.equals("=="))
					output.format("ifeq %s\n", label1);				
				else if (operator.equals("!="))
					output.format("ifne %s\n", label1);
				else
					output.format("ifeq %s\n", label1);
			}
			else if (typeLeft.equals(Type.floatType))
			{
				right.emit(output);
				if (typeRight.equals(Type.intType))
					output.format("i2f\n");
				output.format("fsub\n");
				output.format("f2i\n");
				if (operator.equals("=="))
					output.format("ifeq %s\n", label1);				
				else if (operator.equals("!="))
					output.format("ifne %s\n", label1);
				else
					output.format("ifeq %s\n", label1);
			} else if (typeLeft.equals(Type.booleanType))
			{
				right.emit(output);
				output.format("isub\n");
				if (operator.equals("=="))
					output.format("ifeq %s\n", label1);				
				else if (operator.equals("!="))
					output.format("ifne %s\n", label1);
				else
					output.format("ifeq %s\n", label1);
			} else if (typeLeft.equals(Type.stringType))
			{
				right.emit(output);
				output.format("invokevirtual java/lang/String/equals(Ljava/lang/Object;)Z\n");
				if (operator.equals("=="))
					output.format("ifne %s\n", label1);				
				else if (operator.equals("!="))
					output.format("ifeq %s\n", label1);
				else
					output.format("ifeq %s\n", label1);
			} else
			{
				right.emit(output);
				
				if (operator.equals("=="))
					output.format("if_acmpeq %s\n", label1);				
				else if (operator.equals("!="))
					output.format("if_acmpne %s\n", label1);
				else
					output.format("if_acmpeq %s\n", label1);
			}			
				
			output.format("goto %s\n", label2);
			output.format("%s:\n", label1);			
			output.format("iconst_1\n");
			output.format("goto %s\n", label3);
			output.format("%s:\n", label2);			
			output.format("iconst_0\n");
			output.format("%s:\n", label3);
		}
	}

	@Override
	public int getLine() {
		return line;
	}

	@Override
	public Type getType() {		
		return type;
	}

	@Override
	public void leftEmit(Formatter output) {
		left.leftEmit(output);		
	}

}
